﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using System.Diagnostics;

namespace Mimoza.PluginLayer
{
    /// <summary>
    /// Provides methods for loading plugin from XML.
    /// </summary>
    class PluginLoader
    {
        /// <summary>
        /// Tries to load plugins from specified XML file.
        /// </summary>
        public void Init(string xmlFilename)
        {
            Common.Logger.Log.Trace("Reading file {0}", xmlFilename);

            XmlReader reader = XmlReader.Create(xmlFilename);
            XmlNodeType xmlNodetype = reader.MoveToContent();
            if (reader.NodeType == XmlNodeType.Element)
            {
                switch (reader.LocalName)
                {
                    case "assembly":
                        ReadAssembly(reader, Path.GetDirectoryName(xmlFilename));
                        break;
                    default:
                        break;
                }
            }
        }

        public List<IConfigPlugin> ConfigPlugins
        {
            get { return m_configPlugins; }
        }

        public List<IExecutePlugin> ExecutePlugins
        {
            get { return m_executePlugins; }
        }

        private void ReadAssembly(XmlReader r, string path)
        {
            Debug.Assert(r.NodeType == XmlNodeType.Element);
            Debug.Assert(r.LocalName == "assembly");

            string assemblyFile = r.GetAttribute("file");
            assemblyFile = CombineAssemblyName(path, assemblyFile);

            Common.Logger.Log.Trace("Initializing assembly {0}...", assemblyFile);

            try
            {
                Assembly assembly = Assembly.LoadFrom(assemblyFile);
                
                r.ReadStartElement();
                while(r.Read())
                {
                    if (r.NodeType == XmlNodeType.Element && r.LocalName == "configplugin")
                    {
                        IConfigPlugin plugin = LoadConfigPlugin(r, assembly);
                        if (plugin != null) m_configPlugins.Add(plugin);
                    }
                    if (r.NodeType == XmlNodeType.Element && r.LocalName == "executeplugin")
                    {
                        IExecutePlugin plugin = LoadExecutePlugin(r, assembly);
                        if (plugin != null) m_executePlugins.Add(plugin);
                    }
                }
            }
            catch (System.Exception e)
            {
                Common.Logger.Log.Error("Error in initializing plugin fileName - '" + 
                    assemblyFile + "' Error - " + e.ToString());
            }

            Common.Logger.Log.Trace("Finished initialization of assembly {0}",
                assemblyFile);
        }

        private static string CombineAssemblyName(string path, string assemblyFile)
        {
#if !WindowsCE
            assemblyFile = Path.Combine(path, assemblyFile);
#else
            string assemblyNameCE = Path.GetFileNameWithoutExtension(assemblyFile) + //"CE" + 
                Path.GetExtension(assemblyFile);
            assemblyFile = Path.Combine(path, assemblyNameCE);
#endif
            return assemblyFile;
        }

        private IConfigPlugin LoadConfigPlugin(XmlReader r, Assembly a)
        {
#if WindowsCE
            return null;  
#endif
            Debug.Assert(r.LocalName == "configplugin",
                "XmlReader must be located at <component> element");

            string typeName = r.GetAttribute("type");

            Common.Logger.Log.Trace("PluginLoader Loading plugin {0}", typeName);

            //r.ReadStartElement();
            IConfigPlugin plugin = null;
            try
            {
                plugin = (IConfigPlugin)a.CreateInstance(typeName);
                if (plugin == null)
                {
                    Common.Logger.Log.Error("PluginLoader can't to create plugin {0}",
                        typeName);
                }
            }
            catch(Exception e)
            {
                Common.Logger.Log.Error("PluginLoader error to create plugin - '{0}'. Error - {1}",
                        typeName, e.ToString());

                Debug.Assert(true, "Error in loading plugin - '" + typeName + "'.");
            }

            Common.Logger.Log.Trace("PluginLoader Finished loading plugin");
            return plugin;
        }

        private IExecutePlugin LoadExecutePlugin(XmlReader r, Assembly a)
        {
            Debug.Assert(r.LocalName == "executeplugin",
                "XmlReader must be located at <component> element");

            string typeName = r.GetAttribute("type");

            Common.Logger.Log.Trace("PluginLoader Loading plugin {0}", typeName);

            r.ReadStartElement();
            IExecutePlugin plugin = null;
            try
            {
                plugin = (IExecutePlugin)a.CreateInstance(typeName);
                if (plugin == null)
                {
                    Common.Logger.Log.Error("PluginLoader can't create execute plugin {0}",
                        typeName);
                }
            }
            catch (Exception e)
            {
                Common.Logger.Log.Error("PluginLoader error to create execute plugin Error - {0}",
                        e.ToString());
            }

            Common.Logger.Log.Trace("PluginLoader Finished loading execute plugin");
            return plugin;
        }


        List<IConfigPlugin> m_configPlugins = new List<IConfigPlugin>();
        List<IExecutePlugin> m_executePlugins = new List<IExecutePlugin>();
    }
}
